//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////


#include "precomp.h"

//
// Bearer service names
//
static const DWORD g_rgdwBearerSvcNames[] =
{
    -1,     // RIL_BSVCNAME_UNKNOWN
    0,      // RIL_BSVCNAME_DATACIRCUIT_ASYNC_UDI_MODEM
    1,      // RIL_BSVCNAME_DATACIRCUIT_SYNC_UDI_MODEM
    2,      // RIL_BSVCNAME_PADACCESS_ASYNC_UDI
    3,      // RIL_BSVCNAME_PACKETACCESS_SYNC_UDI
    4,      // RIL_BSVCNAME_DATACIRCUIT_ASYNC_RDI
    5,      // RIL_BSVCNAME_DATACIRCUIT_SYNC_RDI
    6,      // RIL_BSVCNAME_PADACCESS_ASYNC_RDI
    7,      // RIL_BSVCNAME_PACKETACCESS_SYNC_RDI
};
#define NUM_BEARERSVCNAMES  (sizeof(g_rgdwBearerSvcNames) / sizeof(DWORD))


//
// Bearer service connection elements
//
static const DWORD g_rgdwBearerSvcConnElems[] =
{
    -1,     // RIL_BSVCCE_UNKNOWN
    0,      // RIL_BSVCCE_TRANSPARENT
    1,      // RIL_BSVCCE_NONTRANSPARENT
    2,      // RIL_BSVCCE_BOTH_TRANSPARENT
    3,      // RIL_BSVCCE_BOTH_NONTRANSPARENT
};
#define NUM_BEARERSVCCONNELEMS  (sizeof(g_rgdwBearerSvcConnElems) / sizeof(DWORD))



//
//
//
static HRESULT ParseGetBearerServiceOptions(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetBearerServiceOptions);
    UINT nValue;
    UINT i;
    RILBEARERSVCINFO* prbsi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prbsi = (RILBEARERSVCINFO*)AllocBlob(sizeof(RILBEARERSVCINFO));
    if (!prbsi)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prbsi, 0x00, sizeof(RILBEARERSVCINFO));
    prbsi->cbSize = sizeof(RILBEARERSVCINFO);

    // Parse "<prefix>+CBST: <speed>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CBST: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_CONNSPEEDS; i++)
    {
        if (nValue == g_rgdwConnSpeeds[i])
        {
            prbsi->dwSpeed = i;
            break;
        }
    }
    if (NUM_CONNSPEEDS == i)
    {
        // We couldn't match the response with anything
        prbsi->dwSpeed = RIL_SPEED_UNKNOWN;
    }
    prbsi->dwParams |= RIL_PARAM_BSI_SPEED;

    // Parse ",<svc_name>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_BEARERSVCNAMES; i++)
    {
        if (nValue == g_rgdwBearerSvcNames[i])
        {
            prbsi->dwServiceName = i;
            break;
        }
    }
    if (NUM_BEARERSVCNAMES == i)
    {
        // We couldn't match the response with anything
        prbsi->dwServiceName = RIL_BSVCNAME_UNKNOWN;
    }
    prbsi->dwParams |= RIL_PARAM_BSI_SERVICENAME;

    // Parse ",<conn_element><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_BEARERSVCCONNELEMS; i++)
    {
        if (nValue == g_rgdwBearerSvcConnElems[i])
        {
            prbsi->dwConnectionElement = i;
            break;
        }
    }
    if (NUM_BEARERSVCCONNELEMS == i)
    {
        // We couldn't match the response with anything
        prbsi->dwConnectionElement = RIL_BSVCCE_UNKNOWN;
    }
    prbsi->dwParams |= RIL_PARAM_BSI_CONNECTIONELEMENT;

    pBlob = (void*)prbsi;
    cbBlob = sizeof(RILBEARERSVCINFO);

    Error:
    if (FAILED(hr))
    {
        FreeBlob(prbsi);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetBearerServiceOptions(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetBearerServiceOptions);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(DATA_PORT, pHandle, "AT+CBST?\r", CMDOPT_NONE, APIID_GETBEARERSERVICEOPTIONS, ParseGetBearerServiceOptions, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetBearerServiceOptions(DWORD dwParam, const RILBEARERSVCINFO* lpBearerServiceInfo)
{
    TBD_FUNCTION(RILDrv_SetBearerServiceOptions);
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    // Add "AT+CBST="
    (void)strncpyz(szWalk, "AT+CBST=", MAX_ATCMD_LEN - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szWalk);

    // Add "<speed>"
    if (lpBearerServiceInfo->dwParams & RIL_PARAM_BSI_SPEED)
    {
        TBD_ASSERT(RIL_SPEED_64000_TRANSP >= lpBearerServiceInfo->dwSpeed);

        if (RIL_SPEED_UNKNOWN == lpBearerServiceInfo->dwSpeed)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", g_rgdwConnSpeeds[lpBearerServiceInfo->dwSpeed]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }
    else
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", g_rgdwConnSpeeds[RIL_SPEED_9600_V32]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_ATCMD_LEN - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szWalk);

    // Add "<svc_name>"
    if (lpBearerServiceInfo->dwParams & RIL_PARAM_BSI_SERVICENAME)
    {
        TBD_ASSERT(RIL_BSVCNAME_PACKETACCESS_SYNC_RDI >= lpBearerServiceInfo->dwServiceName);

        if (RIL_BSVCNAME_UNKNOWN == lpBearerServiceInfo->dwServiceName)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", g_rgdwBearerSvcNames[lpBearerServiceInfo->dwServiceName]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }
    else
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", g_rgdwBearerSvcNames[RIL_BSVCNAME_DATACIRCUIT_ASYNC_UDI_MODEM]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_ATCMD_LEN - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szWalk);

    // Add "<conn_elem>"
    if (lpBearerServiceInfo->dwParams & RIL_PARAM_BSI_CONNECTIONELEMENT)
    {
        TBD_ASSERT(RIL_BSVCCE_BOTH_NONTRANSPARENT >= lpBearerServiceInfo->dwConnectionElement);

        if (RIL_BSVCCE_UNKNOWN == lpBearerServiceInfo->dwConnectionElement)
        {
            hr = E_INVALIDARG;
            goto Error;
        }

        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u",
                         g_rgdwBearerSvcConnElems[lpBearerServiceInfo->dwConnectionElement]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }
    else
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", g_rgdwBearerSvcConnElems[RIL_BSVCCE_NONTRANSPARENT]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }

    // Add "<CR>"
    (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));
    if (!QueueCmd(DATA_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETBEARERSERVICEOPTIONS, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}


//
//
//
static HRESULT ParseGetRLPOptions(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetRLPOptions);
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILRLPINFO* rgrrlpi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "+CRLP: "
    while (MatchStringBeginning(szRsp, "+CRLP: ", szRsp))
    {
        if (nUsed == nAllocated)
        {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrrlpi, sizeof(RILRLPINFO), nUsed, &nAllocated, DATA_ALLOC_NUMBER))
            {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrrlpi[nUsed], 0x00, sizeof(RILRLPINFO));
        rgrrlpi[nUsed].cbSize = sizeof(RILRLPINFO);

        // Parse "<iws>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        rgrrlpi[nUsed].dwIWS = nValue;
        rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_IWS;

        // Parse ",<mws>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        rgrrlpi[nUsed].dwMWS = nValue;
        rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_MWS;

        // Parse ",<ack_timer>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        rgrrlpi[nUsed].dwAckTimer = nValue;
        rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_ACKTIMER;

        // Parse ",<retr_att>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        rgrrlpi[nUsed].dwRetransmissionAttempts = nValue;
        rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_RETRANSMISSIONATTEMPTS;

        // Parse ","
        if (MatchStringBeginning(szRsp, ",", szRsp))
        {
            // Parse "<version>"
            if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
            {
                goto Continue;
            }
            rgrrlpi[nUsed].dwVersion = nValue;
            rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_VERSION;

            // Parse ","
            if (MatchStringBeginning(szRsp, ",", szRsp))
            {
                // Parse "<reseq_period>"
                if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
                {
                    goto Continue;
                }
                rgrrlpi[nUsed].dwResequencingPeriod = nValue;
                rgrrlpi[nUsed].dwParams |= RIL_PARAM_RPLI_RESEQUENCINGPERIOD;
            }
        }

        // If we didn't see the version, assume 0 (per GSM 07.07, ch6.8)
        if (!(rgrrlpi[nUsed].dwParams & RIL_PARAM_RLPI_VERSION))
        {
            rgrrlpi[nUsed].dwVersion = 0;
            rgrrlpi[nUsed].dwParams |= RIL_PARAM_RLPI_VERSION;
        }

        // Increment the array index
        nUsed++;

        Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp))
        {
            hr = E_FAIL;
            goto Error;
        }
    }

    pBlob = (void*)rgrrlpi;
    cbBlob = nUsed * sizeof(RILRLPINFO);

    Error:
    if (FAILED(hr))
    {
        FreeBlob(rgrrlpi);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetRLPOptions(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetRLPOptions);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(DATA_PORT, pHandle, "AT+CRLP?\r", CMDOPT_NONE, APIID_GETRLPOPTIONS, ParseGetRLPOptions, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetRLPOptions(DWORD dwParam, const RILRLPINFO* lpRlpInfo)
{
    TBD_FUNCTION(RILDrv_SetRLPOptions);
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    // Add "AT+CRLP="
    (void)strncpyz(szWalk, "AT+CRLP=", MAX_ATCMD_LEN - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szWalk);

    // Add "<iws>"
    if (lpRlpInfo->dwParams & RIL_PARAM_RLPI_IWS)
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", lpRlpInfo->dwIWS);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_ATCMD_LEN - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szWalk);

    // Add "<mws>"
    if (lpRlpInfo->dwParams & RIL_PARAM_RLPI_MWS)
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", lpRlpInfo->dwMWS);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_ATCMD_LEN - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szWalk);

    // Add "<ack_timer>"
    if (lpRlpInfo->dwParams & RIL_PARAM_RLPI_ACKTIMER)
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", lpRlpInfo->dwAckTimer);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_ATCMD_LEN - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szWalk);

    // Add "<retr_att>"
    if (lpRlpInfo->dwParams & RIL_PARAM_RLPI_RETRANSMISSIONATTEMPTS)
    {
        (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%u", lpRlpInfo->dwRetransmissionAttempts);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);
    }

    // Delete trailing commas
    while (*(szWalk-1) == ',' && szWalk > szCmd)
    {
        szWalk--;
        *szWalk = '\0';
    }

    // Add "<CR>"
    (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));
    if (!QueueCmd(DATA_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETRLPOPTIONS, NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

